﻿#include <zlib.h>
#include <boost/property_tree/json_parser.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/lexical_cast.hpp>
#include <cmath>

#include "include/Utils.h"
#include "../../../Common/PackageUtil.h"

using namespace boost;

locale *Utils::_p_locale =
    new locale(locale::classic(), new boost::posix_time::time_facet("%Y-%m-%d %H:%M:%s"));
locale *Utils::_p_milli_locale =
    new locale(locale::classic(), new boost::posix_time::time_facet("%Y-%m-%d %H:%M:%s *"));

vector<int> *Utils::_p_id_vector = new vector<int>();
boost::shared_mutex *Utils::_p_id_mutex = new boost::shared_mutex();

void Utils::Str2StrList(string &str, vector<string> &str_list, char splitChar)
{
	str_list.clear();

	if(0 == str.length())
	{
	    return;
	}

	string tmpStr;
	size_t index = str.find(splitChar);
	size_t pos = 0;

	while(string::npos != index)
	{
		str_list.push_back(str.substr(pos, index - pos));
		pos = index + 1;

		index = str.find(splitChar, pos);
	}

	str_list.push_back(str.substr(pos));
}

// Compress zlib data
int Utils::ZCompress(const char *data, unsigned long ndata, char *zdata, unsigned long *nzdata)
{
	z_stream c_stream;
	int err = 0;

	if(!data || ndata <= 0)
	{
		return -1;
	}

	c_stream.zalloc = (alloc_func)0;
	c_stream.zfree = (free_func)0;
	c_stream.opaque = (voidpf)0;

	if(deflateInit(&c_stream, Z_DEFAULT_COMPRESSION) != Z_OK)
	{
		return -1;
	}

	c_stream.next_in = (Bytef *)data;
	c_stream.avail_in = ndata;
	c_stream.next_out = (Bytef *)zdata;
	c_stream.avail_out = *nzdata;

	while (c_stream.avail_in != 0 && c_stream.total_out < *nzdata)
	{
		if(deflate(&c_stream, Z_NO_FLUSH) != Z_OK)
		{
			return -1;
		}
	}

	if(c_stream.avail_in != 0)
	{
		return c_stream.avail_in;
	}

	for (;;)
	{
		if((err = deflate(&c_stream, Z_FINISH)) == Z_STREAM_END)
		{
			break;
		}
		if(err != Z_OK)
		{
			return -1;
		}
	}

	if(deflateEnd(&c_stream) != Z_OK)
	{
		return -1;
	}

	*nzdata = c_stream.total_out;

	return 0;
}



// Compress gzip data
int Utils::GZCompress(const char *data, unsigned long ndata, char *zdata, unsigned long *nzdata)
{
	z_stream c_stream;
	int err = 0;

	if(!data || ndata <= 0)
	{
		return -1;
	}

	c_stream.zalloc = (alloc_func)0;
	c_stream.zfree = (free_func)0;
	c_stream.opaque = (voidpf)0;

	if(deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK)
	{
		return -1;
	}

	c_stream.next_in = (Bytef *)data;
	c_stream.avail_in = ndata;
	c_stream.next_out = (Bytef *)zdata;
	c_stream.avail_out = *nzdata;

	while (c_stream.avail_in != 0 && c_stream.total_out < *nzdata)
	{
		if(deflate(&c_stream, Z_NO_FLUSH) != Z_OK)
		{
			return -1;
		}
	}

	if(c_stream.avail_in != 0)
	{
		return c_stream.avail_in;
	}

	for(;;)
	{
		if((err = deflate(&c_stream, Z_FINISH)) == Z_STREAM_END)
		{
			break;
		}
		if(err != Z_OK)
		{
			return -1;
		}
	}

	if(deflateEnd(&c_stream) != Z_OK)
	{
		return -1;
	}

	*nzdata = c_stream.total_out;

	return 0;
}

// Uncompress zlib data
int Utils::ZDecompress(char *zdata, unsigned long nzdata, char *data, unsigned long *ndata)
{
	int err = 0;
	z_stream d_stream; /* decompression stream */
	d_stream.zalloc = (alloc_func)0;
	d_stream.zfree = (free_func)0;
	d_stream.opaque = (voidpf)0;
	d_stream.next_in = (Bytef *)zdata;
	d_stream.avail_in = 0;
	d_stream.next_out = (Bytef *)data;

	if(inflateInit(&d_stream) != Z_OK)
	{
		return -1;
	}

	while (d_stream.total_out < *ndata && d_stream.total_in < nzdata)
	{
		d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */

		if((err = inflate(&d_stream, Z_NO_FLUSH)) == Z_STREAM_END)
		{
			break;
		}
		if(err != Z_OK)
		{
			return -1;
		}
	}

	if(inflateEnd(&d_stream) != Z_OK)
	{
		return -1;
	}

	*ndata = d_stream.total_out;

	return 0;
}

// Uncompress gzip data
int Utils::GZDecompress(char *zdata, unsigned long nzdata, char *data, unsigned long *ndata)
{
	int err = 0;
	z_stream d_stream = {0}; /* decompression stream */
	static char dummy_head[2] =
	{
		0x8 + 0x7 * 0x10, (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF
	};

	d_stream.zalloc = (alloc_func)0;
	d_stream.zfree = (free_func)0;
	d_stream.opaque = (voidpf)0;
	d_stream.next_in = (Bytef *)zdata;
	d_stream.avail_in = 0;
	d_stream.next_out = (Bytef *)data;

	if(inflateInit2(&d_stream, -MAX_WBITS) != Z_OK)
	{
		return -1;
	}

	//if(inflateInit2(&d_stream, 47) != Z_OK) return -1;
	while (d_stream.total_out < *ndata && d_stream.total_in < nzdata)
	{
		d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
		if((err = inflate(&d_stream, Z_NO_FLUSH)) == Z_STREAM_END)
		{
			break;
		}

		if(err != Z_OK)
		{
			if(err == Z_DATA_ERROR)
			{
				d_stream.next_in = (Bytef *)dummy_head;
				d_stream.avail_in = sizeof(dummy_head);

				if((err = inflate(&d_stream, Z_NO_FLUSH)) != Z_OK)
				{
					return -1;
				}
			}
			else
			{
				return -1;
			}
		}
	}

	if(inflateEnd(&d_stream) != Z_OK)
	{
		return -1;
	}

	*ndata = d_stream.total_out;

	return 0;
}

int Utils::GetUniqueId()
{
	boost::unique_lock<boost::shared_mutex> lock(*_p_id_mutex);

	if(0 == _p_id_vector->size())
	{
		return -1;
	}

	int id = _p_id_vector->back();
	_p_id_vector->pop_back();

	return id;
}

int Utils::TimeStr2MilliSec(string &time_str)
{
	stringstream ss(time_str);
	ss.imbue(*_p_milli_locale);
	boost::posix_time::ptime pt;
	ss >> pt;

	boost::posix_time::ptime pt1 = boost::posix_time::second_clock::local_time();
	boost::posix_time::time_duration diff = pt - pt1;
	return diff.ticks() * 1000 /
			boost::posix_time::time_duration::rep_type::ticks_per_second;
}

int Utils::TimeStr2Seconds(string &time_str)
{
	stringstream ss(time_str);
	ss.imbue(*_p_locale);
	boost::posix_time::ptime pt;
	ss >> pt;

	boost::posix_time::ptime pt1 = boost::posix_time::second_clock::local_time();
	boost::posix_time::time_duration diff = pt - pt1;
	return diff.ticks() /
			boost::posix_time::time_duration::rep_type::ticks_per_second;
}

string Utils::MilliSec2TimeStr(int milli_seconds)
{
    boost::posix_time::ptime pt = boost::posix_time::second_clock::local_time();
    pt = pt + boost::posix_time::millisec(milli_seconds);

    ostringstream os;
    os.imbue(*_p_milli_locale);
    os << pt;

    return os.str();
}

string Utils::Seconds2TimeStr(int seconds)
{
	boost::posix_time::ptime pt = boost::posix_time::second_clock::local_time();
	pt = pt + boost::posix_time::seconds(seconds);

	ostringstream os;
	os.imbue(*_p_locale);
	os << pt;

	return os.str();
}

void Utils::CreateMultiPlayerTree(vector<int> &player_list, ptree &format_tree)
{
    string str = "";
    for(unsigned int i = 0; i < player_list.size(); i++)
    {
        str += lexical_cast<string>(player_list[i]);
        str += "|";
    }

    if(str.length() > 1)
    {
        str.erase(str.length() - 1, 1);
    }

    format_tree.put("playerList", str);
}

//void Utils::CreateMultiPlayerBuf(vector<int> &player_list, int &len, char *buf)
//{
//    Common::MulticastHead *p_multi_head = (Common::MulticastHead *)buf;
//	p_multi_head->player_num = player_list.size();
//
//	int *p_player = (int *)((char *)p_multi_head + sizeof(struct Common::MulticastHead));
//	for(unsigned int i = 0; i < player_list.size(); i++)
//	{
//		*p_player = player_list[i];
//		p_player++;
//	}
//
//	len = sizeof(struct Common::MulticastHead) + sizeof(int) * player_list.size();
//}

//int Utils::CreateMulticastInfo(vector<int> &player_list, ptree &tree, int character_id,
//                                  int format, int &len, char *buf)
//{
//	ostringstream os;
//
//	try
//	{
//		write_json(os, tree);
//	}
//	catch(boost::exception &ex)
//	{
//		return 1;
//	}
//
//	char *data_buf = buf + sizeof(struct Common::DataHead);
//
//	int data_len;
//	switch(format)
//	{
//	case Common::Binary:
//		return 1;
//
//	case Common::Json:
//	{
//		memcpy(data_buf, os.str().c_str(), os.str().length());
//		data_len = os.str().length();
//	}
//		break;
//
//	case Common::GZipJson:
//	{
//		GZCompress(os.str().c_str(), os.str().length(), data_buf, (unsigned long *)(&data_len));
//	}
//		break;
//
//	case Common::ZlibJson:
//	{
//		ZCompress(os.str().c_str(), os.str().length(), data_buf, (unsigned long *)(&data_len));
//	}
//		break;
//
//	default:
//		break;
//	}
//
//	data_len = (data_len + 3) / 4;
//
//	Common::DataHead *p_data_head = (Common::DataHead *)buf;
//	p_data_head->character_id = character_id;
//	p_data_head->format = format;
//	p_data_head->length = data_len;
//
//	Common::MulticastHead *p_multi_head = (Common::MulticastHead *)(buf +
//        sizeof(struct Common::DataHead) + data_len * 4);
//	p_multi_head->player_num = player_list.size();
//
//	int *p_player = (int *)((char *)p_multi_head + sizeof(struct Common::MulticastHead));
//	for(unsigned int i = 0; i < player_list.size(); i++)
//	{
//		*p_player = player_list[i];
//		p_player++;
//	}
//
//	len = sizeof(struct Common::DataHead) + p_data_head->length * 4 + sizeof(struct Common::MulticastHead) +
//		sizeof(int) * player_list.size();
//
//    return 0;
//}

int Utils::Distence(int x1, int y1, int x2, int y2)
{
    return (int)sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
